package com.datx.wallet.service;

import com.datx.wallet.base.BaseResponse;
import com.datx.wallet.config.WalletProperties;
import com.datx.wallet.constants.WalletConstants;
import com.datx.wallet.crypto.EncryptUtils;
import com.datx.wallet.entity.ExchangeTrsOrder;
import com.datx.wallet.entity.ExchangeUserWallet;
import com.datx.wallet.entity.dto.ExchangeBlockDto;
import com.datx.wallet.entity.dto.ExchangeTransactionDto;
import com.datx.wallet.mapper.ExchangeTrsOrderMapper;
import com.datx.wallet.mapper.ExchangeUserWalletMapper;
import com.datx.wallet.service.BaseBizService;
import com.datx.wallet.service.CommonService;
import com.exchange.wallet.bitcoin.rpcclient.Bitcoin;
import com.exchange.wallet.bitcoin.rpcclient.BitcoinException;
import com.exchange.wallet.bitcoin.rpcclient.BitcoinRPCClient;
import com.exchange.wallet.bitcoin.rpcclient.BitcoinUtil;
import lombok.extern.slf4j.Slf4j;
import net.logstash.logback.encoder.org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

/**
 * BtcService Created with IntelliJ IDEA.
 * User: liulinhui
 * Date: 2018/8/28
 * Time: 14:47
 * Description: BtcService
 */
@Service
@Slf4j
public class BtcService extends BaseBizService {

    @Resource
    private WalletProperties walletProperties;
    @Resource
    private BitcoinRPCClient rpcClient;
    @Resource
    private EncryptUtils encryptUtils;
    @Resource
    private ExchangeUserWalletMapper exchangeUserWalletMapper;
    @Resource
    private ExchangeTrsOrderMapper exchangeTrsOrderMapper;
    @Resource
    private CommonService commonService;

    /**
     * 新建账户
     *
     * @param userId datx地址
     * @param asset BTC
     * @return
     * @throws Exception
     */
    @Override
    public ExchangeUserWallet createAccount(String userId, String asset) throws Exception {
        String address = rpcClient.getNewAddress(String.valueOf(userId));
        String privKey = rpcClient.dumpPrivKey(address);
        return ExchangeUserWallet.builder().address(address)
                .coinName(walletProperties.getCoinName())
                .coinType(walletProperties.getCoinName())
                .userId(userId)
                .privateKey(encryptUtils.encrypt(privKey))
                .build();
    }

    @Override
    public BigInteger getLatestBlockNum() throws Exception {
        return new BigInteger(String.valueOf(rpcClient.getBlockCount()));
    }

    @Override
    public ExchangeBlockDto getBlockByNum(BigInteger height) throws Exception {
        String blockHash = rpcClient.getBlockHash(height.intValue());
        if (StringUtils.isBlank(blockHash)) {
            throw new Exception("获取区块hash：" + height + "失败");
        }
        //获取区块
        Bitcoin.Block block = rpcClient.getBlock(blockHash);
        if (block == null) {
            throw new Exception("获取区块：" + height + "失败");
        }
        List<String> txids = block.tx();
        ExchangeBlockDto blockDto = ExchangeBlockDto.builder()
                .hash(blockHash)
                .height(height)
                .trsNum(txids.size())
                .build();
        log.info(">>>>>>>>>>开始解析区块：height=" + block.height() + "<<<<<<<<<<");
        log.info("区块" + block.height() + "包含" + block.tx().size() + "笔交易");
        //遍历区块中的交易
        List<ExchangeTransactionDto> txList = new ArrayList<>();
        String coinName = walletProperties.getCoinName();
        for (String txid : txids) {
            log.info(">>>>>>>>>>TXID=" + txid + "<<<<<<<<<<");
            try {
                Bitcoin.RawTransaction transaction = rpcClient.getRawTransaction(txid);
                List<Bitcoin.RawTransaction.Out> outs = transaction.vOut();
                if (null == outs) continue;
                for (Bitcoin.RawTransaction.Out out : outs) {
                    if (null == out.scriptPubKey()) continue;
                    Bitcoin.RawTransaction.Out.ScriptPubKey scriptPubKey = out.scriptPubKey();
                    if (scriptPubKey == null) continue;
                    List<String> addresses = scriptPubKey.addresses();
                    if (addresses == null || addresses.size() <= 0) continue;
                    String address = addresses.get(0);
                    if (StringUtils.isBlank(address)) continue;
                    ExchangeTransactionDto transactionDto = ExchangeTransactionDto.builder()
                            .amount(new BigDecimal(out.value()))
                            .to(address)
                            .hash(txid)
                            .trsType(WalletConstants.TRADE_RECHARGE)
                            .coinName(coinName)
                            .coinType(coinName)
                            .build();
                    ExchangeTrsOrder exchangeTrsOrder = exchangeTrsOrderMapper.selectByHashAndToAddress(txid, address);
                    if (exchangeTrsOrder != null) {
                        //移除已经处理过的充值交易
                        if (WalletConstants.TRADE_RECHARGE == exchangeTrsOrder.getTransactionType()) {
                            continue;
                        }
                        //交易必须正在进行中
                        if (WalletConstants.TRADE_PENDING != exchangeTrsOrder.getStatus()) {
                            continue;
                        } else {
                            transactionDto.setTrsType(exchangeTrsOrder.getTransactionType());
                            txList.add(transactionDto);
                            continue;
                        }
                    }
                    ExchangeUserWallet exchangeUserWallet = exchangeUserWalletMapper.selectWalletByAddressAndCoinName(address, coinName);
                    if (exchangeUserWallet != null) {
                        transactionDto.setUserId(exchangeUserWallet.getUserId());
                        txList.add(transactionDto);
                    }
                }
            } catch (BitcoinException e) {
                log.info("无此" + txid + "TXID");
            }
        }
        blockDto.setTrs(txList);
        return blockDto;
    }

    @Override
    public ExchangeTransactionDto getTxByHash(String hash) {
        return null;
    }

    @Override
    public BigDecimal getBalanceByAddress(String address, String asset) throws Exception {
        String account = rpcClient.getAccount(address);
        log.info("account=" + account + ",address=" + address);
        return new BigDecimal(rpcClient.getBalance(account));
    }

    @Override
    public BaseResponse transfer(String to, BigDecimal amount, String coinName, BigDecimal fee) {
        log.info("transfer:address={},amount={},fee={}", to, amount, fee);
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            return BaseResponse.ERROR("额度须大于0");
        }
        if (fee == null) {
            fee = walletProperties.getCoinFactory().get(coinName).getFee();
        }
        try {
            String txId = BitcoinUtil.sendTransaction(rpcClient, to, amount, fee);
            commonService.recordExchangeTrsOrder("", to, amount, coinName, coinName, fee, txId, WalletConstants.TRADE_RECHARGE_ALL, null);
            return BaseResponse.OK(txId);
        } catch (BitcoinException e) {
            log.error(ExceptionUtils.getFullStackTrace(e));
            return BaseResponse.ERROR(e.getMessage());
        }
    }

    @Override
    public BaseResponse withdraw(String from, String to, BigDecimal amount, String coinName, BigDecimal fee, String userId) {
        log.info("withdraw:address={},amount={},fee={}", to, amount, fee);
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            return BaseResponse.ERROR("额度须大于0");
        }
        if (fee == null) {
            fee = walletProperties.getCoinFactory().get(coinName).getFee();
        }
        try {
            String txId = BitcoinUtil.sendTransaction(rpcClient, to, amount, fee);
            if (StringUtils.isNotBlank(txId)) {
                commonService.recordExchangeTrsOrder("", to, amount, coinName, coinName, fee, txId, WalletConstants.TRADE_WITHDRAW, userId);
            }
            return BaseResponse.OK(txId);
        } catch (BitcoinException e) {
            log.error(ExceptionUtils.getFullStackTrace(e));
            return BaseResponse.ERROR(e.getMessage());
        }
    }
}
